//---------------------------------------------------------------------------------
// Copyright (c) 2008 VoiceLynx Project
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
//     1. The origin of this software must not be misrepresented; you must not
//        claim that you wrote the original software. If you use this software
//        in a product, an acknowledgment in the product documentation would be
//        appreciated but is not required.
//
//     2. Altered source versions must be plainly marked as such, and must not be
//        misrepresented as being the original software.
//
//     3. This notice may not be removed or altered from any source
//        distribution.
//---------------------------------------------------------------------------------

#include "VLPacketLogic.h"
#include <algorithm>


VLPacketLogic::VLPacketLogic()
{

}

VLPacketLogic::~VLPacketLogic()
{

}

void VLPacketLogic::setPacketTranslator( VLPacketTranslator &translator )
{
  mPacketTranslator = &translator;
}

//
// Client
//
VLClientPacketLogic::VLClientPacketLogic() 
: mPeer(0)
{

}

void VLClientPacketLogic::configurePacketTypes( bool init )
{
  REG_PACKET_HANDLER(VL_PT_STARTVOICE, VLClientPacketLogic::onReceiveStartVoice);
  REG_PACKET_HANDLER(VL_PT_STOPVOICE, VLClientPacketLogic::onReceiveStopVoice);
  REG_PACKET_HANDLER(VL_PT_VOICEDATA, VLClientPacketLogic::onReceiveVoiceData);
  REG_PACKET_HANDLER(VL_PT_USER_JOINED, VLClientPacketLogic::onReceiveUserJoined);
  REG_PACKET_HANDLER(VL_PT_USER_CHANGED_CHANNEL, VLClientPacketLogic::onReceiveUserChangedChannel);
  REG_PACKET_HANDLER(VL_PT_USER_LEFT, VLClientPacketLogic::onReceiveUserLeft);
}

VLPeerPtr VLClientPacketLogic::createPeer()
{
  VLPeerPtr peer(0);

  return peer;
}

void VLClientPacketLogic::onConnect(VLPeer *peer)
{
  mPeer = peer;

  VLAuthenticationPacket p(mUsername, mPassword);

  sendPacket(peer, p);
}

void VLClientPacketLogic::onDisconnect(VLPeer *peer)
{
  mPeer = 0;
}

void VLClientPacketLogic::onReceiveAuthenticationResponse( VLPeer *peer, VLAuthenticationResponsePacket &packet )
{
  VLChannelStateRequestPacket p;
  sendPacket(peer, p);
}

void VLClientPacketLogic::onStartVoice()
{
  VLStartVoicePacket p(VL_USERID_SELF);

  sendPacket(mPeer, p);
}

void VLClientPacketLogic::onStopVoice()
{
  VLStopVoicePacket p(VL_USERID_SELF);

  sendPacket(mPeer, p);
}

void VLClientPacketLogic::onVoiceData(VLByteArrayPtr encodedData)
{
  VLVoiceDataPacket p(VL_USERID_SELF, 0, encodedData);

  sendPacket(mPeer, p);
}

void VLClientPacketLogic::onReceiveStartVoice(VLPeer *peer, VLStartVoicePacket &packet)
{
  VL_TRACE("Starting voice stream from user " << packet.userId);
}

void VLClientPacketLogic::onReceiveStopVoice(VLPeer *peer, VLStopVoicePacket &packet)
{
  VL_TRACE("Stopping voice stream from user " << packet.userId);
}

void VLClientPacketLogic::onReceiveVoiceData(VLPeer *user, VLVoiceDataPacket &packet)
{
  VL_TRACE("  Received voice data  (" << packet.getVoiceData()->size() << " B)  from user " << packet.userId);
}

void VLClientPacketLogic::onReceiveUserJoined(VLPeer *peer, VLUserJoinedPacket &packet)
{
  VL_TRACE("User " << packet.userName << " has joined.");
  
  VLUserPtr user(new VLUser());
  packet.applyToUser(*user.get());

  VL_ASSERT(mUsers.find(user->userId) == mUsers.end());
  mUsers[user->userId] = user.get();

  user.release(); // We're not doing this as part of the above line as the user.get()
                  // seems to happen before what's in the []s
}

void VLClientPacketLogic::onReceiveUserLeft( VLPeer *peer, VLUserLeftPacket &packet )
{
  VL_TRACE("User " << packet.userId << " has left.");
  
  VLUserIdToUserMap::iterator iter = mUsers.find(packet.userId);

  if (iter != mUsers.end())
  {
    // Todo fill this in, then make users show up in UI
    delete iter->second;
    mUsers.erase(iter);
  }
  else
  {
    VL_ASSERT_ALWAYS();
  }
}

void VLClientPacketLogic::onReceiveUserChangedChannel( VLPeer *peer, VLUserChangedChannelPacket &packet )
{
  VL_TRACE("User " << packet.userId << " has changed to channel ?.");
}

void VLClientPacketLogic::onReceiveChannelState( VLPeer *peer, VLChannelStatePacket &packet )
{
  VL_TRACE("Received channel state");
}

//
// Server
//
VLServerPacketLogic::VLServerPacketLogic() 
: mNextUserId(1) // Don't start at 0 for debug purposes
{

}

void VLServerPacketLogic::configurePacketTypes( bool init )
{
  REG_PACKET_HANDLER(VL_PT_STARTVOICE, VLServerPacketLogic::onReceiveStartVoice);
  REG_PACKET_HANDLER(VL_PT_STOPVOICE, VLServerPacketLogic::onReceiveStopVoice);
  REG_PACKET_HANDLER(VL_PT_VOICEDATA, VLServerPacketLogic::onReceiveVoiceData);
  REG_PACKET_HANDLER(VL_PT_AUTHENTICATION, VLServerPacketLogic::onReceiveAuthentication);
}

VLPeerPtr VLServerPacketLogic::createPeer()
{
  // Create a user and peer
  VLPeerPtr peer(new VLPeer());
  VLUserPtr user(new VLUser());

  // Setup the user
  user->peer = peer.get();
  user->connectTime = time(0);
  user->isAuthenticated = false;

  // Point the peer at the user
  peer->user = user.get();

  mUsers.push_back(user.release());

  return peer;
}

void VLServerPacketLogic::onConnect(VLPeer *peer)
{
  
}

void VLServerPacketLogic::onDisconnect(VLPeer *peer)
{
  if (peer->user != 0)
  {
    VLUserLeftPacket leftPacket(peer->user->userId);

    broadcastPacket(leftPacket, peer);

    VLUserList::iterator iter = std::find(mUsers.begin(), mUsers.end(), peer->user);
    if (iter != mUsers.end())
    {
      delete peer->user;
      mUsers.erase(iter);
    }

    peer->user = 0;
  }
}

void VLServerPacketLogic::onStartVoice()
{

}

void VLServerPacketLogic::onStopVoice()
{

}

void VLServerPacketLogic::onVoiceData( VLByteArrayPtr encodedData )
{

}

void VLServerPacketLogic::process()
{
  std::vector<VLUser *> disconnectedUsers;

  for (VLUserList::iterator user = mUsers.begin();
    user != mUsers.end(); ++user)
  {
    VLU32 currentTime = time(0);

    // Disconnect anyone who is not authenticated and has been connected more than 5 seconds
    if ((*user)->isAuthenticated == false && (*user)->connectTime < currentTime - 5)
    {
      disconnectUser(*user, VL_DR_AUTH_REQUIRED);
      disconnectedUsers.push_back(*user);
    }
  }

  for (std::vector<VLUser *>::iterator iter = disconnectedUsers.begin(); 
    iter != disconnectedUsers.end(); ++iter)
  {
    mUsers.erase(std::find(mUsers.begin(), mUsers.end(), *iter));
    delete *iter;
  }
}

void VLServerPacketLogic::onReceiveStartVoice( VLPeer *peer, VLStartVoicePacket &packet )
{
  VL_TRACE("Starting voice stream from " << peer);

  if (peer->user->isAuthenticated)
  {
    // Set the userid to the source of the packet
    packet.userId = peer->user->userId;

    broadcastPacket(packet, peer);
  }
}

void VLServerPacketLogic::onReceiveStopVoice( VLPeer *peer, VLStopVoicePacket &packet )
{
  VL_TRACE("Stopping voice stream from " << peer);
  
  if (peer->user->isAuthenticated)
  {
    // Set the userid to the source of the packet
    packet.userId = peer->user->userId;

    broadcastPacket(packet, peer);
  }
}

void VLServerPacketLogic::onReceiveVoiceData(VLPeer *peer, VLVoiceDataPacket &packet)
{
  VL_TRACE("  received voice data from " << peer);
  
  if (peer->user->isAuthenticated)
  {
    // Set the userid to the source of the packet
    packet.userId = peer->user->userId;

    broadcastPacket(packet, peer);
  }
}

void VLServerPacketLogic::onReceiveAuthentication(VLPeer *peer, VLAuthenticationPacket &packet)
{
  if (authenticateUser(packet))
  {
    peer->user->isAuthenticated = true;
    peer->user->userId = mNextUserId++;
    peer->user->userName = packet.name;

    VLUserJoinedPacket joinedPacket(*peer->user);

    joinedPacket.userId = peer->user->userId;

    broadcastPacket(joinedPacket, peer);

    VLAuthenticationResponsePacket authPacket(true, peer->user->userId);

    sendPacket(peer, authPacket);
  }
  else
  {
    disconnectFromPeer(peer, VL_DR_AUTH_FAILED);
  }
}

void VLServerPacketLogic::onReceiveChannelStateRequest( VLPeer *peer, VLChannelStateRequestPacket &packet )
{
  VL_TRACE("Received channel state request");
}

bool VLServerPacketLogic::authenticateUser( VLAuthenticationPacket &packet )
{
  // Let anyone and everyone connect
  return true;
}

void VLServerPacketLogic::disconnectUser(VLUser *user, VLEDisconnectReason reason)
{
  disconnectFromPeer(user->peer, reason);
}
